"""
Module to support Set operations.

Sets are a group of unordered unique elements.

Last Modified: $Id: set.py 14 2005-05-17 20:36:03Z gustavo $

@author: U{Gustavo Sverzut Barbieri <barbieri@gmail.com>}
@license: GNU GPL
@copyright: (C) 2005 Gustavo Sverzut Barbieri
@version: $Rev: 14 $
"""









class SetException( Exception ):
    """Set Exception.

    Every Set exceptions inherit from this.
    """
    def __init__( self, element, exception ):
        self.element   = element
        self.exception = exception
    # __init__()

    def __str__( self ):
        return "%s: %s" % ( self.__class__.__name__, exception )
    # __str__()
# SetException






class Set( object ):
    """Basic Set.

    You can perform basic set operations, like add, delete, iterate, verify if
    a set contains a given element.

    Creation:

    >>> s = Set( [ 1, 2 ] )
    >>> print s
    ( 1, 2 )


    Add and remove elements:

    >>> s.add( 3 )
    >>> print s
    ( 1, 2, 3 )
    >>> s.delete( 1 )
    >>> print s
    ( 2, 3 )


    Iterate over elements:

    >>> s.items()
    [2, 3]
    >>> for i in s:
    ...   print i
    2
    3


    Check for elements in set:

    >>> 2 in s
    True
    >>> del s[ 3 ]
    >>> 3 in s
    False
    >>> s.add( 10 )
    >>> len( s )
    2
    """
    def __init__( self, elements=None ):
	self.__data = {}

	if elements:
	    for e in elements:
		self.add( e )
    # __init__()


    def add( self, element ):
        """Adds an element to set.

        @param element: any object that must be part of this set.
        """
	self.__data[ element ] = True
    # add()


    def delete( self, element ):
        """Removes an element from set.

        @precondition: element must already be part of this set.

        @param element: any object that must be removed of this set.

        @raises SetException: if element is not in this set.
        """
        try:
            del self.__data[ element ]
        except KeyError, e:
            raise SetException( element, e )
    # delete()
    __delitem__ = delete


    def items( self ):
        """Get a list of elements in this set.

        @return: list of elements in an arbitrary order.
        @rtype: L{list} of elements
        """
	return self.__data.keys()
    # items()


    def iteritems( self ):
        """Iterate over set elements.

        Generator that yields elements in an arbitrary order.
        """
	return self.__data.iterkeys()
    # iteritems()
    __iter__ = iteritems


    def has( self, element ):
        """Check if element is in set.

        @return: True is element is in set, False otherwise.
        @rtype: L{bool}
        """
	return element in self.__data
    # has()
    __contains__ = has


    def __nonzero__( self ):
	if self.__data:
	    return True
	else:
	    return False
    # __truth__()


    def __len__( self ):
	return len( self.__data )
    # __len__()


    def __repr__( self ):
	return "Set( [ %s ] )" % ", ".join( [ repr( x )
					      for x in self.items() ] )
    # __repr__()


    def __str__( self ):
	return "( %s )" % ", ".join( [ str( x ) for x in self.items() ] )
    # __str__()
# Set
